;=================================================
GAME_DATA_INDEX_ENABLE      =   0       ;是否使用节目数据 Bank 索引
GAME_DATA_INDEX_BANK        =   $3F     ;节目数据 Bank 索引 的 Bank 号
GAME_DATA_INDEX_ADDR        =   $A000   ;节目数据 Bank 索引 的 Bank 地址
;-------------------------------------------------
GAME_DATA_BANK              =   CUSTOM_DATA_BANK     ;最终切换数据所在Bank
GAME_DATA_ADDR              =   CUSTOM_DATA_ADDR    ;最终切换数据起始地址
GAME_SELECT_PROC_ADDR       =   $0400   ;切换节目地址
CHR_RAM_WRITE_PROC_ADDR     =   $0300   ;写入CHR-VRAM处理地址
SELECT_CLEAR_ADDR           =   $01E0   ;清除RAM代码地址
;=================================================
;卡带寄存器
GAME_REG_0                  =   $5000
GAME_REG_1                  =   GAME_REG_0 + 1
GAME_REG_2                  =   GAME_REG_1 + 1
GAME_REG_3                  =   GAME_REG_2 + 1
GAME_REG_4                  =   GAME_REG_3 + 1
GAME_REG_5                  =   GAME_REG_4 + 1
GAME_REG_6                  =   GAME_REG_5 + 1
GAME_REG_7                  =   GAME_REG_6 + 1
GAME_REG_COUNT              =   8

    .RSSET                  $20
;=================================================
;占用零页内存
Game_Chr_Offset_L           .RS 1
Game_Chr_Offset_H           .RS 1
Game_Chr_Size               .RS 1
Game_Flags                  .RS 1
;------------------------------------------------------------
Game_Reg_0_For_Chr          .RS 1
Game_Reg_1_For_Chr          .RS 1
Game_Reg_2_For_Chr          .RS 1
Game_Reg_3_For_Chr          .RS 1
Game_Reg_4_For_Chr          .RS 1
Game_Reg_5_For_Chr          .RS 1
Game_Reg_6_For_Chr          .RS 1
Game_Reg_7_For_Chr          .RS 1
;------------------------------------------------------------
Game_Reg_0_For_Prg          .RS 1
Game_Reg_1_For_Prg          .RS 1
Game_Reg_2_For_Prg          .RS 1
Game_Reg_3_For_Prg          .RS 1
Game_Reg_4_For_Prg          .RS 1
Game_Reg_5_For_Prg          .RS 1
Game_Reg_6_For_Prg          .RS 1
Game_Reg_7_For_Prg          .RS 1
;------------------------------------------------------------
FC_Game_Index_L             =   FC_Item_Index_L ;节目索引低位
FC_Game_Index_H             =   FC_Item_Index_H ;节目索引高位
FC_Game_Data_Tmp            .RS 1       ;临时缓冲数据
FC_Game_Data_Count          .RS 1       ;数据计数
FC_Game_Word_L              .RS 1       ;临时缓冲数据
FC_Game_Word_H              .RS 1       ;数据计数
FC_Game_Data_L              .RS 1       ;临时缓冲数据
FC_Game_Data_H              .RS 1       ;数据计数

;========================================
;获取节目数据所在bank
Get_Game_Config_From_Bank_Index
    LDA #$00
    STA <FC_Game_Data_L
    STA <FC_Game_Data_H
    
    .IF GAME_DATA_INDEX_ENABLE
    
        ;切换到存放配置bank号数据所在的bank
        LDA #GAME_DATA_INDEX_BANK & BANK_DATA_MASK
        JSR Bank_Switch_A000

        ;取到bank
        LDA <FC_Game_Index_L
        CLC
        ADC #LOW(GAME_DATA_INDEX_ADDR)
        STA <FC_Game_Data_L
        LDA <FC_Game_Index_H
        ADC #HIGH(GAME_DATA_INDEX_ADDR)
        STA <FC_Game_Data_H

    .ENDIF

    ;切换到节目数据所在bank
    LDY #$00
    LDA [FC_Game_Data_L],Y
    CLC
    ADC #GAME_DATA_BANK & BANK_DATA_MASK
    JSR Bank_Switch_A000

    ;得到起始
    LDA GAME_DATA_ADDR
    STA <FC_Game_Word_L
    LDA GAME_DATA_ADDR + 1
    STA <FC_Game_Word_H

    ;得到起始索引
    LDA <FC_Game_Index_L
    SEC
    SBC <FC_Game_Word_L
    STA <FC_Game_Data_L
    LDA <FC_Game_Index_H
    SBC <FC_Game_Word_H
    STA <FC_Game_Data_H

    ASL <FC_Game_Data_L
    ROL <FC_Game_Data_H

    ;得到配置数据起始地址
    LDA <FC_Game_Data_L
    CLC
    ADC #LOW(GAME_DATA_ADDR) + 2
    STA <FC_Game_Data_L
    LDA <FC_Game_Data_H
    ADC #HIGH(GAME_DATA_ADDR)
    STA <FC_Game_Data_H
    RTS

;========================================
Get_Game_Config
    JSR Get_Game_Config_From_Bank_Index
    
    ;得到配置数据地址
    LDY #$00
    LDA [FC_Game_Data_L],Y
    PHA
    INY
    LDA [FC_Game_Data_L],Y
    STA <FC_Game_Data_H
    PLA
    STA <FC_Game_Data_L

    ;CHR 8KB偏移
    LDY #$00
.Get_Game_Chr_Config
    LDA [FC_Game_Data_L],Y
    STA <Game_Chr_Offset_L
    STA Game_Reg_1_For_Chr
    INY 
    LDA [FC_Game_Data_L],Y
    STA <Game_Chr_Offset_H
    STA <Game_Reg_0_For_Chr

    ;CHR大小
    INY
    LDA [FC_Game_Data_L],Y
    STA Game_Chr_Size

    ;Flags
    INY
    LDA [FC_Game_Data_L],Y
    STA Game_Flags

    ;CHR 8KB偏移 -> 16KB偏移
    LSR <Game_Reg_0_For_Chr
    ROR <Game_Reg_1_For_Chr

    INY

    ;最终PRG寄存器配置
    LDX #$00
.Get_Game_Prg_Config
    LDA [FC_Game_Data_L],Y
    STA <Game_Reg_0_For_Prg,X
    INY
    INX
    CPX #GAME_REG_COUNT
    BCC .Get_Game_Prg_Config
    RTS

;==================================================
;选择节目处理
Select_Game_Proc
    LDX #$00
.Rom_Select_Chr_Offset
    LDA <Game_Reg_0_For_Chr,X
    STA GAME_REG_0,X
    INX
    CPX #$06
    BCC .Rom_Select_Chr_Offset

    LDA #$00
    STA <FC_Game_Data_Tmp
    STA <FC_Game_Data_Count

;拷贝 CHR 数据 到 CHR-RAM
.Write_CHR_To_Vram
    LDA #$06
    STA MAPPER_REG_BANK_CTRL

    ;如果8KB偏移地址是偶数, 则CHR-ROM从0开始, 否则从1开始, 因为8KB CHR的 ROM会组合成16KB放在一起
    LDA <Game_Chr_Offset_L
    AND #%00000001
    CLC
    ADC <FC_Game_Data_Tmp
    STA MAPPER_REG_BANK_DATA

    LDA <FC_Game_Data_Count
    ASL A
    ASL A
    ASL A
    TAY
.Mapping_Vram_0000_0FFF;设置PPU地址 $0000-$07FF, MMC3 8000写入$00,$01
    LDX #$00
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA ;0000-07FF
    INX
    INY
    INY
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA ;0800-0FFF
    INY
.Mapping_Vram_1000_1FFF;设置PPU地址 $1000-$1FFF, MMC3 8000写入$02,$03,$04,$05
    INX
    INY
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA ;1400-17FF
    CPX #$05
    BCC .Mapping_Vram_1000_1FFF

    LDA #$80
    STA CHR_RAM_WRITE_PROC_ADDR + 2

    LDA <Game_Chr_Size
    BEQ .Rom_Prg_Bank_Init
    LDA PPU_STATUS
    LDY #$00
    STY PPU_ADDRESS
    STY PPU_ADDRESS
    
    LDX #$20
    JSR CHR_RAM_WRITE_PROC_ADDR

    INC <FC_Game_Data_Tmp
    INC <FC_Game_Data_Count
    LDA <FC_Game_Data_Count
    CMP <Game_Chr_Size
    BCC .Write_CHR_To_Vram
    
.Rom_Prg_Bank_Init;初始化 PRG Bank $8000-BFFF
    LDA #$06
    STA MAPPER_REG_BANK_CTRL
    LDA #$00
    STA MAPPER_REG_BANK_DATA
    LDA #$07
    STA MAPPER_REG_BANK_CTRL
    LDA #$01
    STA MAPPER_REG_BANK_DATA

.Rom_Chr_Bank_Init_0000_0FFF;初始化 CHR Bank $0000-0FFF
    LDX #$00
    LDY #$00
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA
    INX
    LDY #$02
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA
    
    LDY #$04
.Rom_Chr_Bank_Init_1000_1FFF;初始化 CHR Bank $1000-1FFF
    INX
    INY
    STX MAPPER_REG_BANK_CTRL
    STY MAPPER_REG_BANK_DATA
    CPX #$05
    BCC .Rom_Chr_Bank_Init_1000_1FFF
 
.Rom_Select_Prg_Offset
    LDX #$00
.Rom_Select_Prg_Offset_Set
    LDA <Game_Reg_0_For_Prg,X
    STA GAME_REG_0,X
    INX
    CPX #GAME_REG_COUNT
    BCC .Rom_Select_Prg_Offset_Set
    JMP SELECT_CLEAR_ADDR
Select_Game_Proc_End

;==================================================
;写入CHR-VRAM处理
Write_Vram_Proc
Write_Vram_Proc_Write
    LDA $8000,Y
    STA $2007
    INY
    BNE Write_Vram_Proc_Write
    INC CHR_RAM_WRITE_PROC_ADDR + 2
    DEX
    BNE Write_Vram_Proc_Write
    RTS
Write_Vram_Proc_End

;==================================================
;选择节目后清理RAM处理
Clear_Ram_Proc
    LDX #$00
    TXA
.Write_Zero
    STA <$00,X
    STA $0200,X
    STA $0300,X
    STA $0400,X
    STA $0500,X
    STA $0600,X
    STA $0700,X
    INX
    BNE .Write_Zero
    JMP [$FFFC]
Clear_Ram_Proc_End

;==================================================
;选择游戏
Select_Game
    LDA #$00
    STA PPU_CTRL
    LDA #$00
    STA PPU_MASK
.Nmi_Wait
    LDA PPU_STATUS
    BPL .Nmi_Wait

;清除调色板
.Palette_Clear
    LDA #$3F
    STA $2006
    LDA #$00
    STA $2006
    LDY #$20
    LDA #$0F
.Write_Pal_Data
    STA PPU_DATA
    DEY
    BNE .Write_Pal_Data
    
;清理音频
.Stop_Sound;停止音乐
    LDX #$00
    LDA #$00
.Stop_Sound_Write
    STA $4000,X
    INX
    CPX #$14
    BCC .Stop_Sound_Write
    STA APU_STATUS
    
;清理RAM
    LDX #$00
    TXA
.Write_Zero
    STA $0100,X
    STA $0200,X
    STA $0300,X
    STA $0400,X
    STA $0500,X
    STA $0600,X
    STA $0700,X
    INX
    BNE .Write_Zero
 
    ;拷贝选择节目的代码到内存
    LDX #$00
.Select_GameCopy
    LDA Select_Game_Proc,x
    STA GAME_SELECT_PROC_ADDR,X
    INX
    CPX #Select_Game_Proc_End - Select_Game_Proc
    BCC .Select_GameCopy
 
    ;清理切换节目端口
    LDA #$00
    LDX #$00
.Game_Temp_Reg_Clear
    STA <Game_Reg_0_For_Chr,X
    STA <Game_Reg_0_For_Prg,X
    INX
    CPX #08
    BCC .Game_Temp_Reg_Clear

    JSR Get_Game_Config

    ;拷贝选择节目后清理内存的代码到内存
    LDX #$00
.Select_Game_Ram_Clear_Copy
    LDA Clear_Ram_Proc,x
    STA SELECT_CLEAR_ADDR,X
    INX
    CPX #Clear_Ram_Proc_End - Clear_Ram_Proc
    BCC .Select_Game_Ram_Clear_Copy
    
    ;拷贝CHR-VRAM写入程序
    LDX #$00
.Select_Vram_Proc_Copy
    LDA Write_Vram_Proc,x
    STA CHR_RAM_WRITE_PROC_ADDR,X
    INX
    CPX #Write_Vram_Proc_End - Write_Vram_Proc
    BCC .Select_Vram_Proc_Copy
    
    LDX #$FF
    TXS
    JMP GAME_SELECT_PROC_ADDR

;==================================================
;菜单大小初始化
Menu_Size_Init
    LDX #$00
.Write
    LDA .Data,X
    STA GAME_REG_0,X
    INX
    CPX #GAME_REG_COUNT
    BCC .Write
    RTS
.Data
    ;    PPPPPPPP
    .DB %00000000
    ;    PPPPPPPP
    .DB %00000000
    ;    AMMMMMMM 14
    .DB %01111000;0000000 -> 1111111 & 0001000 -> 0001000  78 08 & 1111111 = 78 128 KB
    ;    BBBCCCCC
    .DB %00000000
    ;    DDDEEEEE
    .DB %00000000
    ;    CDDEEEWW
    .DB %00000000
    ;    FFFMMMMM
    .DB %00010100
    ;    LMTRRSNO
    .DB %00000111